﻿
using Boilen.Primitives.Members;
using System.Collections.Generic;
using System.Linq;
using System.Windows;


namespace Boilen.Primitives {

    /// <summary>
    /// Represents the metadata arguments for registering a dependency property.
    /// </summary>
    public sealed class DependencyPropertyMetadata {

        private readonly string[] MetadataTypeNames;
        private readonly List<string> metadataArguments_ = new List<string>( );

        private readonly bool silverlight_;
        private DependencyPropertyMetadataKind metadataKind_;


        /// <summary>
        /// Gets a value indicating whether the metadata arguments are for use on silverlight or WPF.
        /// </summary>
        public bool Silverlight { get { return this.silverlight_; } }

        /// <summary>
        /// Gets the kind of metadata required by the added metadata arguments.
        /// </summary>
        public DependencyPropertyMetadataKind MetadataKind { get { return this.metadataKind_; } }

        /// <summary>
        /// Gets or sets the extensions used by the dependency property initializer.
        /// </summary>
        public Extensions UsedExtensions { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether a single argument should be declared inline or indented.
        /// </summary>
        public bool InlineSingleArgument { get; set; }

        /// <summary>
        /// Gets or sets the name of the coercion handler for the dependency property.
        /// </summary>
        public string CoerceHandler { get; set; }

        /// <summary>
        /// Gets or sets the name of the changed handler for the dependency property.
        /// </summary>
        public string ChangedHandler { get; set; }

        /// <summary>
        /// Gets or sets the name of the validation handler for the dependency property.
        /// </summary>
        public string ValidationHandler { get; set; }


        /// <summary>
        /// Initializes a new <see cref="DependencyPropertyMetadata"/> instance.
        /// </summary>
        public DependencyPropertyMetadata( TypeRepository typeRepository, bool silverlight ) {
            this.silverlight_ = silverlight;

            this.metadataKind_ = DependencyPropertyMetadataKind.Base;

            this.MetadataTypeNames = new string[1 + (int)DependencyPropertyMetadataKind.Framework];
            this.MetadataTypeNames[(int)DependencyPropertyMetadataKind.Base] = typeRepository.GetTypeName( typeof( PropertyMetadata ) );
            this.MetadataTypeNames[(int)DependencyPropertyMetadataKind.UI] = typeRepository.GetTypeName( typeof( UIPropertyMetadata ) );
            this.MetadataTypeNames[(int)DependencyPropertyMetadataKind.Framework] = typeRepository.GetTypeName( typeof( FrameworkPropertyMetadata ) );
        }

        /// <summary>
        /// Adds the specified metadata argument, updating <see cref="MetadataKind"/> as necessary to accomodate the argument's required minimum.
        /// </summary>
        public void AddMetadataArgument( string argument, DependencyPropertyMetadataKind minimumMetadataKind ) {
            if( string.IsNullOrEmpty( argument ) )
                return;

            this.metadataArguments_.Add( argument );
            if( minimumMetadataKind > this.MetadataKind ) { this.metadataKind_ = minimumMetadataKind; }
        }

        /// <summary>
        /// Writes the "new MetadataKind" expression, specifying all of the added metadata arguments.
        /// </summary>
        public void Write( ICodeWriter writer ) {
            string metadataTypeName = this.MetadataTypeNames[(int)this.MetadataKind];
            writer.Write( "new {0}", metadataTypeName );

            using( Enclose.Parenthesis( writer ) ) {
                if( this.InlineSingleArgument && this.metadataArguments_.Count == 1 )
                    writer.Write( this.metadataArguments_.Single( ) );
                else
                    using( Enclose.NewLine( writer ) )
                    using( Enclose.Indent( writer ) ) {
                        Util.Iterate( this.metadataArguments_, ( i, last ) => writer.WriteLine( "," ), ( i, a ) => writer.Write( a ) );
                    }
            }

            string validateHandler = this.Silverlight ? null : this.ValidationHandler;
            if( validateHandler != null ) {
                writer.WriteLine( "," );
                writer.Write( validateHandler );
            }
        }

    }

}
